home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-04-08 | 38.6 KB | 1,066 lines | [TEXT/MPCC] |
- /* DriverDoDriverIO.c */
- /*
- * DriverDoDriverIO.c
- * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
- * Edit History
- * 1994.10.24 D3 First DDK release (System A2C1)
- * 1994.12.06 A5 Adding enhancements to the driver:
- * √ Folder and file reorganization (including changes to MakeFile)
- * √ Retrieve initiator bus ID from the System Registry (NVRAM property)
- * There appears to be a bug (either in my iteration code or in
- * the Name Registry, so this has been disabled temporarily.
- * √ (The TestDriver application will set the property for testing)
- * √ Implement Supersede/Replace (was compiled out)
- * √ Call SynchronizeIO in NCRRunScript - this needs to be checked
- * by engineering to make sure I did this correctly, as it can't
- * easily be checked by minimal testing.
- * √ Bug: Expansion Manager I/O routines need the device base
- * address. (Rewrote GetPCICardBaseAddress and fixed expansion
- * Manager calls).
- * -- below the line -- done only if there's time --
- * √ Handle discontiguous physical memory in PrepareMemoryForIO
- * Support disconnect/reselect and, thereby, concurrent I/O.
- * Redo the low-level routines so they can be used in a sample SIM.
- * √ Implement driver logging using a native "Audit" package.
- * √ Retrieve the power consumption information from the registry.
- * -- Waiting for A5
- * √ Implement Secondary Interrupts
- * 1994.12.12 A4 Changes for the A4 DDK Release:
- * √ MappingTable is now IOPreparationTable
- * 1994.12.18 A5 DriverGestalt.h is missing from this header release. Copied an
- * old version to the folder "::PCICIncludes Temp" - this may change.
- * 1994.12.30 A5+ • NCRDriver.h (the public file) now includes some system headers
- * so it can be used independently. No substantive changes.
- * • NCRRunScript.c now compiles if interrupts are disabled. No
- * substantive changes.
- * • Moved the actual scripts from NCRRunScript.c to a separate file.
- * • Removed scatter-gather routines from DriverPrepMemoryUtilities.c
- * as they haven't been tested. Revised PrepareSimpleMemoryArea to
- * follow the logic (if any) in the the develop article.
- * • Revised script preparation for clarity (and debugging). The
- * script now initializes constant registers.
- * • Select (at compilation) either Memory or I/O access; not both.
- * • Bug: CTEST0 was being written as a long, it is a byte.
- * • Turn off timers to prevent spurious interrupts.
- * • Set DSA to a dummy value for Bus Reset script - it doesn't use
- * the DSA.
- * • Removed the non-interrupt spin-loop from the script runner.
- * Added (temporary) Software Interrupt to see if this fixes the
- * QueueSecondaryInterrupt problem (it didn't). Turned on the
- * watchdog timer code as it no longer crashes.
- * • Parameterized watchdog timer (it hangs in A5).
- * 1995.01.04 • Added TEMP_TEST to strip down the algorithm. This turns off
- * PrepareMemoryForIO (the physical address is set to the logical
- * address) and creates a minimal (successful) script.
- * 1995.01.13 • Secondary interrupts work.
- * • Correctly return power consumption by parsing the registry.
- * • Removed many DebugStr's and removed TEMP_TEST.
- * 1995.01.22 Upgraded to A6. Added the LogLibrary functions. Removed all
- * SecondaryInterrupt conditional compilations. Redid the way
- * that KillIO terminates the current request as it called a
- * secondary interrupt handler from "interrupt level" after a
- * timeout. Log some information about the current request.
- * • The log shows that the NCR chip -- sometimes -- jumps into
- * the middle of a script instruction. Can this be a bus problem?
- * Hmm, the problem went away on its own.
- * 1995.01.23 • Fixed the NVRAM bug: I was using RegPropertyName rather than
- * RegPropertyNameBuf. The former is a pointer to a character string,
- * while the latter is a buffer that can hold a property name.
- * 1995.01.24 • Removed the NVRAM bit from the driver options Control call.
- * Added set/get initiator id (PBControl sets it, PBStatus gets it).
- * • Cleaned up NVRAM handling. DriverReplaceCmd now fetches the
- * initiator ID, if it can be found.
- * • Completely rewrote PrepareMemoryForIO handling so the driver
- * can re-called from an IOCompletion routine. This precludes
- * allocating memory in the driver (outside of open/close). This
- * is a massive restructuring of the driver. The new organization
- * is described below.
- * • Removed the DriverOptions control command: it's served its
- * purpose, and no longer illustrates any driver-specific functions.
- * • Removed the script log control command. The script log is now
- * reset on each command. Hmm, the script log is outliving its
- * usefulness, now that the LogLibrary works. Maybe it'll be
- * the next to go.
- * • Removed the temporary string copy definitions.
- * 1995.02.03 Fixed some bugs: my PrepareMemoryForIO code used the wrong
- * variable in one place, leading to memory corruption. Also,
- * the atomic operation used to recover the current parameter
- * block from the per-request record (in CompleteThisRequest)
- * had a re-entrancy bug.
- * 1995.02.06 Synchronous I/O seems to work correctly. Asynchronous I/O
- * hangs randomly, possibly because of bugs in the NCR-specific
- * code. Added DeviceProbe and AAPL,addresses processing.
- * • Removed the NCR logging -- I'm relying on the LogLibrary
- * for ongoing status logging.
- * • Enabled watchdog timers -- the param block changed.
- * 1995.02.08 • Significant changes to support the new driver organization:
- * DMA buffers can only be passed in PBRead or PBWrite commands,
- * and buffers must be marked with the ioMapBuffer flag in the
- * ioPosMode field.
- * • Hmm, Asynch I/O now seems to work.
- * 1995.02.21 Missing break at Close switch statement. Added partial
- * I/O preparation.
- * 1995.03.08 • Continue testing PrepareMemoryForIO.
- * Single-stepping scripts hang in Bus Reset
- * • Added DelayForHardware for 12 PCI clock tick stalls.
- * • Removed my SwapLong: now using driver services routine.
- * • Support DriverGestaltOn(). Replace now stores the driver
- * refNum and deviceID.
- * • Remove TempCheckpointIO
- * 1995.03.17 • Reimplemented all Name Registry routines to make them
- * independent of the NCR driver.
- * • Switched to a different NCR card. This has id "pci1000,4"
- * and a different CTEST3 value. It differs from the original
- * card in that it is 8-bit only (no wide, no fast). It's
- * also quite a bit cheaper.
- * • The memory leak problem may be in some other application,
- * It doesn't seem to appear in my latest testing.
- * • Randomly (after a few thousand trials), the watchdog
- * timer fires immediately, rather than after the actual
- * time (2 seconds in the test program). In reviewing the code,
- * there appears to be a potential race condition in the sequence:
- * Start watchdog timer then start I/.O, if this is executed
- * from "task" level. To prevent this, I moved all I/O start
- * to the secondary interrupt task, where it should have been
- * all along.
- * • SetInterruptTimer requires an interval, rather than an extact
- * finish time. This will be fixed in the documentation.
- * • An "engineering build" of the ROMS may have fixed the
- * SetInterruptTimer problem. Time will tell.
- * • Chip initialization was changed to use a read/modify/write
- * sequence to preserve the bits that were set by the Expansion
- * Manager.
- * • The A8 ROMS require that SendSoftwareInterrupt be run
- * from the Secondary Interrupt Handler.
- * • There is a minor bug in the SendSoftwareInterrupt sequence:
- * CancelTimer should record the amount of time remaining so that
- * a subsequent timer restart does not reset the watchdog to its
- * original value.
- * 1995.03.29 • Still chasing timer and interrupt problems.
- * • Restored SetInterruptTimer's documented behavior (timer
- * fires at an epoch, not after an interval.
- * • Before re-calling for partial preparation, we must update
- * the IOTable's firstPrepared.
- * • The Secondary Interrupt handler was confusing administration
- * status (from CheckpointIO, for example), with the ioResult value
- * that should be passed back to me caller. Hence, "selection timeout"
- * was returning "Check Condition" status.
- * • Use the driver refNum (converted to an OSType) as the default
- * log entry identifier.
- * 1995.04.04 • Added a 'sysz' resource (1 Mbyte) to work around a problem
- * when logging is enabled: if three devices are installed, only one
- * or two actually get activated. Also, device testing seems to
- * hang in strange ways. This was not seen with the non-logging
- * version. The value may be incorrect. This worked fine. Switching
- * to 256 Kbytes -- still working reasonably well.
- * 1995.04.05 • Debugged memory move test. Everything looks correct, but the
- * script doesn't work. I noticed that the actual script is not
- * 32-bit aligned. Added #pragma options align=power to the headers
- * to force the memory move script to be 32-bit aligned. If this
- * fails, I'll add a padding halfword. Yup, that did it. Unfortunately,
- * this means that all programs using the NCR driver must be
- * recompiled. One more bug required a slightly more complex
- * control flow through the Secondary Interrupt routine, which is
- * starting to look a bit messy. Removing (with an editor) all
- * of the test and debug junk would probably make it look better.
- * • Revised GetDeviceLogicalAddress to use the structure in PCI.h --
- * no substantive changes.
- * • Some compilers are rumored to handle functions returning
- * structures incorrectly. Store UpTime in a volatile local variable
- * if it is to be used in a function call.
- * • Added a function to write a message into the registry if
- * the initialization sequence failed. It seems that I might have
- * had a tiny error in the revised GetDeviceLogicalAddress.
- *
- * Preparing Memory
- * There are three active mapping tables; one for the script, one for the
- * per-request data (including data shared with the NCR chip), and one for the
- * user's I/O request. They are allocated as follows:
- * Script: When the driver is opened, the script is "prepared" and a
- * physical mapping table allocated. Also, a physical mapping table
- * will allocated for user I/O requests. This uses a maximum transfer
- * count parameter that is currently compiled in, but could easily
- * be stored in the Name Registry. This data is released when the
- * driver is closed.
- * PerRequest: One of these will be allocated when the driver is opened. A future
- * modification would allocate some larger number to support fully-
- * concurrent I/O requests (i.e. to support disconnect/reselect).
- * This table is released when the driver is closed.
- * User: The user table is built into the PerRequest record. It will be
- * initialized and checkpointed on each I/O request. This will not
- * allocate or release memory.
- *
- * Misfeatures
- * The NCR Script design does not permit concurrent I/O operations, which is
- * not sufficient for a SCSI device.
- *
- * Open bugs
- * ••• There is a problem in my understanding of the NCR chip that prevents KillIO
- * from working well. It does work, however, but sometimes leaves the bus busy.
- */
- /* .___________________________________________________________________________________.
- | DoDriverIO, after modification, will be useful for other drivers. It shows the |
- | overall structure of the driver "top-level," calling handler functions for each |
- | driver operation. You will have to replace much of the actual driver operation |
- | with code specific to your particular purpose, but the general structure should |
- | remain the same. |
- | |
- | These routines, after modification, will be useful for other drivers. |
- | DriverDoDriverIO Driver mainline. Calls hardware specific routines |
- | or operation-specific functions as necessary. |
- | NameRegistryUtilities Store and retrieve information from the Name Registery |
- | DriverPCIBusUtilities Utilities to read and write data on the PCI bus. There |
- | are functions for the Configuration registers, and for |
- | both Expansion Manager and direct memory access to |
- | chip registers. |
- | DriverDebugSupport Some very crude routines for debugging. |
- | DriverGestaltHandler Process the PBStatus driver gestalt request |
- | |
- | These routines are specific to the NCR SCSI interface. You will be able to borrow |
- | code from them, but will probably rewrite these from scratch for your driver's |
- | specific needs. |
- | NCRChipManager Utilities to set values in the NCR chip. This shows how |
- | to use the Expansion Manager routines to read and write |
- | device registers. |
- | NCRStartScript Prepare to execute a SCSI bus operation. |
- | NCRRunScript Execute a SCSI bus operation. This file includes the |
- | interrupt service routine. |
- | |
- | There is a lot of debugging code in this driver. My programming approach is to |
- | leave debugging code in the source files, but remove it automatically (by |
- | #ifdef's) from shipping versions. This is especially important for asynchronous |
- | device drivers, as they are incompatible with interactive debugging. |
- .___________________________________________________________________________________.
- */
-
-
- /* DriverDoDriverIO is a device driver framework that operates using the new driver
- * architecture.
- *
- * The following overall code-flow paths are possible:
- * Immediate Commands:
- * Process command. Return an error if the request may stall (but do
- * not execute or otherwise delay the request).
- * Return the final I/O request status to the caller.
- * Other commands:
- * Process the command.
- * If it is complete (success or failure) without any delay, call
- * IOCommandIsComplete(...) to return a result to the caller
- * and return noErr to the Device Manager.
- * If it requires an asynchronous operation, start the operation and
- * return noErr to the Device Manager. When the asynchronous
- * operation completes, it will call IOCommandIsComplete.
- *
- * Build instructions (MetroWerks PPC)
- * Processor
- * Readonly strings checked
- * Linker
- * Initialization CFMInitialize
- * Main DoDriverIO
- * Termination CFMTerminate
- * PEF
- * Export Symbols Use .exp file to export the following globals
- * TheDriverDescription
- * DoDriverIO
- * Expand uninitialized data checked
- * Project
- * Project Type Shared Library
- * File Name NCRDriver
- * File Creator PSSD -- Our registered creator type
- * File Type shlb
- */
-
- #include "NCRDriverPrivate.h"
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * We must export a global DriverDescription record that the new Device Manager
- * will use to configure and instantiate our driver.
- */
- /*
- * There is a bug in some versions of the MPW PPCC compiler that cause an error if you
- * try to expand fully-bracketed structures -- this structure is constructed from
- * several other structures.
- */
- DriverDescription TheDriverDescription = {
- /*
- * Signature info
- */
- kTheDescriptionSignature, /* OSType driverDescSignature */
- kInitialDriverDescriptor, /* DriverDescVersion driverDescVersion */
- /*
- * DriverType driverType - these are defined in NCR53C825.h
- */
- kPCIDeviceNamePString, /* Name of NCR 53C825 hardware */
- kPCIRevisionID, kVersionMinor, /* NumVersion version */
- kVersionStageValue, kVersionRevision,
- /*
- * DriverOSRuntime driverOSRuntimeInfo
- */
- 0 /* RuntimeOptions driverRuntime */
- | (1 * kDriverIsLoadedUponDiscovery) /* Loader runtime options */
- | (1 * kDriverIsOpenedUponLoad) /* Opened when loaded */
- | (0 * kDriverIsUnderExpertControl) /* I/O expert handles loads/opens */
- | (0 * kDriverIsConcurrent) /* Not concurrent yet */
- | (0 * kDriverQueuesIOPB), /* Not internally queued yet */
- kDriverNamePString, /* Str31 driverName (OpenDriver param) */
- 0, 0, 0, 0, 0, 0, 0, 0, /* UInt32 driverDescReserved[8] */
- /*
- * DriverOSService Information. This section contains a vector count followed by
- * a vector of structures, each defining a driver service.
- */
- 1, /* ServiceCount nServices */
- /*
- * DriverServiceInfo service[0]
- */
- kServiceCategoryNdrvDriver, /* OSType serviceCategory */
- kNdrvTypeIsGeneric, /* OSType serviceType */
- kVersionMajor, kVersionMinor, /* NumVersion serviceVersion */
- kVersionStageValue, kVersionRevision, /* NumVersion serviceVersion */
- };
-
- /*
- * All driver-global information is in a structure defined in NCRDriverPrivate.
- * Note that "modern" drivers do not have access to their dce. In native Power PC
- * environments, the global world is created by the Code Fragment Manager (hmm,
- * perhaps it is created by CFMInitialize).
- */
- DriverGlobal gDriverGlobal;
-
- OSErr CFMInitialize(void);
- OSErr CFMTerminate(void);
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * CFMInitialize is called by the Code Fragment Manager. This would be a good
- * place to make sure that we should load in the first place -- For example,
- * if necessary hardware isn't present, or there is an operating system conflict,
- * we can return an error and not take up any system memory.
- */
- OSErr
- CFMInitialize(void)
- {
- #if USE_LOG_LIBRARY
- LogRecordPtr logRecordPtr;
-
- logRecordPtr = MakeLogRecord(kPCIDeviceNameCString, 256);
- WriteLogEntry(logRecordPtr, 'CFMi', LogStringFormat, "\pDriver CFMInitialize");
- #endif
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * CFMTerminate is called by the Code Fragment Manager when the code segment
- * is about to be trashed. It could dispose of any global data that is not
- * managed by DoDriverIO.
- */
- OSErr
- CFMTerminate(void)
- {
- #if USE_LOG_LIBRARY
- LogRecordPtr logRecordPtr;
-
- logRecordPtr = GetLogRecordPtr(kPCIDeviceNameCString);
- WriteLogEntry(logRecordPtr, 'CFMt', LogStringFormat, "\pDriver CFMTerminate");
- #endif
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DoDriverIO
- *
- * In the new driver environment, DoDriverIO performs all driver
- * functions. It is called with the following parameters:
- * IOCommandID A unique reference for this driver request. In
- * the emulated environment, this will be the ParamBlkPtr
- * passed in from the Device Manager.
- * IOCommandContents A union structure that contains information for the
- * specific request. For the emulated environment, this
- * will contain the following:
- * Initialize Driver RefNum and the name registry id for this driver.
- * Finalize Driver RefNum and the name registry id for this driver.
- * Others The ParamBlkPtr
- * IOCommandCode A switch value that specifies the required function.
- * IOCommandKind A bit-mask indicating Synchronous, Asynchronous, and Immediate
- *
- * For Synchronous and Immediate commands, DoDriverIO returns the final status to
- * the Device Manager. For Asynchronous commands, DoDriverIO may return kIOBusyStatus.
- * If it returns busy status, the driver promises to call IOCommandIsComplete when
- * the transaction has completed.
- */
- OSErr
- DoDriverIO(
- AddressSpaceID addressSpaceID,
- IOCommandID ioCommandID,
- IOCommandContents ioCommandContents,
- IOCommandCode ioCommandCode,
- IOCommandKind ioCommandKind
- )
- {
- OSErr status;
-
- /*
- * Note: Initialize, Open, KillIO, Close, and Finalize are either synchronous
- * or immediate. Read, Write, Control, and Status may be immediate,
- * synchronous, or asynchronous.
- */
- Trace(DoDriverIO);
- switch (ioCommandCode) {
- case kInitializeCommand: /* Always immediate */
- //** SysDebugStr("\p Driver Initialize");
- #if USE_LOG_LIBRARY
- /*
- * Because we can (indeed, do) have multiple cards running from the same
- * device driver source, we set the default Log identifier to a unique
- * string by converting the device refNum to a right-justified OSType.
- */
- {
- Str15 work;
- StringPtr workPtr;
-
- work[4] = 0;
- AppendSigned(&work[4], ioCommandContents.initialInfo->refNum);
- workPtr = &work[5];
- switch (work[4]) { /* Kids, */
- case 0: work[1] = ' '; --workPtr; /* don't */
- case 1: work[2] = ' '; --workPtr; /* try */
- case 2: work[3] = ' '; --workPtr; /* this */
- case 3: work[4] = ' '; --workPtr; /* at */
- default: break; /* home! */
- }
- BlockCopy(workPtr, &GLOBAL.logIdentifier, sizeof (OSType));
- }
- GLOBAL.logRecordPtr = MakeLogRecord(kPCIDeviceNameCString, 256);
- LogString("\pDoDriverIO (Initialize)");
- PreserveLogRecord(GLOBAL.logRecordPtr, FALSE); /* Wrap-around */
- #endif
- status = DriverInitializeCmd(
- addressSpaceID, ioCommandContents.initialInfo);
- CheckStatus(status, "\pInitialize failed");
- break;
- case kFinalizeCommand: /* Always immediate */
- //** SysDebugStr("\p Driver Finalize");
- LogString("\pDoDriverIO (Finalize)");
- status = DriverFinalizeCmd(ioCommandContents.finalInfo);
- break;
- case kSupersededCommand:
- //** SysDebugStr("\p Driver Superseded");
- LogString("\pDoDriverIO (Supersede)");
- status = DriverSupersededCmd(ioCommandContents.supersededInfo, FALSE);
- break;
- case kReplaceCommand: /* replace an old driver */
- //** SysDebugStr("\p Driver Replace");
- LogString("\pDoDriverIO (Replace)");
- status = DriverReplaceCmd(
- addressSpaceID, ioCommandContents.replaceInfo, FALSE);
- break;
- case kOpenCommand: /* Always immediate */
- //** SysDebugStr("\p Driver Open");
- LogString("\pDoDriverIO (Open)");
- status = DriverOpenCmd(addressSpaceID, ioCommandContents.pb);
- CheckStatus(status, "\pOpen failed");
- break;
- case kCloseCommand: /* Always immediate */
- //** SysDebugStr("\p Driver Close");
- LogString("\pDoDriverIO (Close)");
- status = DriverCloseCmd(ioCommandContents.pb);
- break;
- case kControlCommand:
- //** SysDebugStr("\p Driver Control");
- LogDecimal(
- ((CntrlParam *) ioCommandContents.pb)->csCode, "\pDoDriverIO (Control)");
- status = DriverControlCmd(
- addressSpaceID,
- ioCommandID,
- ioCommandKind,
- (CntrlParam *) ioCommandContents.pb
- );
- break;
- case kStatusCommand:
- //** SysDebugStr("\p Driver Status");
- LogDecimal(
- ((CntrlParam *) ioCommandContents.pb)->csCode, "\pDoDriverIO (Status)");
- status = DriverStatusCmd(
- ioCommandID,
- ioCommandKind,
- (CntrlParam *) ioCommandContents.pb
- );
- break;
- case kReadCommand:
- //** SysDebugStr("\p Driver Read");
- status = DriverReadCmd(
- addressSpaceID,
- ioCommandID,
- ioCommandKind,
- ioCommandContents.pb
- );
- break;
- case kWriteCommand:
- //** LogString("\p Driver Write");
- status = DriverWriteCmd(
- addressSpaceID,
- ioCommandID,
- ioCommandKind,
- ioCommandContents.pb
- );
- break;
- case kKillIOCommand: /* Always immediate */
- //** SysDebugStr("\p Driver Killio");
- LogString("\pDoDriverIO (KillIO)");
- status = DriverKillIOCmd(ioCommandContents.pb);
- break;
- default:
- //** SysDebugStr("\p Driver Bogus");
- LogHex((UInt32) ioCommandCode, "\pUnknown DoDriverIO IOCommandCode");
- status = paramErr;
- break;
- }
- /*
- * Force a valid result for immediate commands -- they must return a valid
- * status to the Driver Manager: returning kIOBusyStatus would be a bug..
- * Non-immediate commands return a status from the lower-level routine. If the
- * status is kIOBusyStatus, we just return -- an asynchronous I/O completion
- * routine will eventually complete the request. If it's some other status, the
- * lower-level routine has completed a non-immediate task, so we call
- * IOCommandIsComplete and return its (presumably noErr) status.
- */
- if ((ioCommandKind & kImmediateIOCommandKind) != 0) {
- ; /* Immediate commands return the operation status */
- }
- else if (status == kIOBusyStatus) {
- /*
- * An asynchronous operation is in progress. The driver handler promises
- * to call IOCommandIsComplete when the operation concludes.
- */
- status = noErr;
- }
- else {
- /*
- * Normal command that completed synchronously. Dequeue the user's
- * parameter block.
- */
- status = IOCommandIsComplete(ioCommandID, status);
- }
- return (status);
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverInitializeCmd
- *
- * The New Driver Manager calls this when the driver is first opened.
- */
- OSErr
- DriverInitializeCmd(
- AddressSpaceID addressSpaceID,
- DriverInitInfoPtr driverInitInfoPtr
- )
- {
- OSErr status;
- ByteCount unusedAreaLength;
- static const Nanoseconds gNsecClockTick12 = { 0, 12 * 33 };
-
- Trace(DriverInitializeCmd);
- GLOBAL.pageSize = GetLogicalPageSize();
- GLOBAL.pageMask = GLOBAL.pageSize - 1;
- GLOBAL.refNum = driverInitInfoPtr->refNum;
- GLOBAL.deviceEntry = driverInitInfoPtr->deviceEntry;
- /*
- * Compute 12 PCI clock ticks in AbsoluteTime units. Currently, we
- * know that the PCI clock cycle is 33 nsec/tick. In the future, we
- * should retrieve the PCI clock speed from the Name Registry.
- */
- GLOBAL.clockTick12 = NanosecondsToAbsolute(gNsecClockTick12);
- GLOBAL.msec250 = DurationToAbsolute(250 * durationMillisecond);
- status = InstallDriverInterruptFunction(
- &GLOBAL.deviceEntry,
- PCIInterruptServiceRoutine,
- &GLOBAL.interruptSetMember,
- &GLOBAL.interruptSetRefcon,
- &GLOBAL.interruptServiceFunction,
- &GLOBAL.interruptEnableFunction,
- &GLOBAL.interruptDisableFunction
- );
- CheckStatus(status, "\pInstallDriverInterruptFunction failed");
- if (status == noErr) {
- status = GetDeviceLogicalAddress(
- &GLOBAL.deviceEntry,
- #if USE_MEM_ACCESS
- kPCIMemoryBaseRegister,
- #else
- kPCIIOBaseRegister,
- #endif
- &GLOBAL.pciCardBaseAddress,
- &unusedAreaLength
- );
- CheckStatus(status, "\pGetPCICardBaseAddress failed");
- }
- if (status == noErr)
- status = NCRInitializeChip();
- if (status == noErr)
- status = DriverReplaceCmd(
- addressSpaceID,
- (DriverReplaceInfoPtr) driverInitInfoPtr,
- TRUE
- );
- if (status != noErr && GLOBAL.interruptDisableFunction != NULL) {
- (*GLOBAL.interruptDisableFunction)(
- GLOBAL.interruptSetMember,
- GLOBAL.interruptSetRefcon
- );
- GLOBAL.interruptDisableFunction = NULL;
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverReplaceCmd
- *
- * We are replacing an existing driver -- or are completing an initialization sequence.
- * Retrieve any state information from the Name Registry (we have none), install
- * our interrupt handlers, and activate the device.
- *
- * We don't use the calledFromInitialize parameter, but it's here so that a driver can
- * distinguish between initialization (fetch only the NVRAM parameter) and replacement
- * (fetch state information that may be left-over from the previous incantation).
- */
- OSErr
- DriverReplaceCmd(
- AddressSpaceID addressSpaceID,
- DriverReplaceInfoPtr driverReplaceInfoPtr,
- Boolean calledFromInitialize
- )
- {
- OSErr status;
- NCRDriverInitiatorIDParam pb;
-
- Trace(DriverReplaceCmd);
- UNUSED(addressSpaceID);
- UNUSED(calledFromInitialize);
- GLOBAL.refNum = driverReplaceInfoPtr->refNum;
- GLOBAL.deviceEntry = driverReplaceInfoPtr->deviceEntry;
- PublishDriverDebugInfo();
- CLEAR(pb);
- GLOBAL.initiatorID = kSCSIInitiatorID;
- InitializeSCSIInitiatorID();
- //** For debugging, defer PatchScript until the first NCR operation so the test
- //** tool can dump the un-compiled script.
- //** status = NCRPatchScript();
- status = noErr;
- if (status == noErr)
- status = DriverGestaltOn(GLOBAL.refNum);
- if (status == noErr)
- status = PrepareMemoryForScript(addressSpaceID);
- if (status == noErr)
- status = CreatePerRequestRecord(addressSpaceID);
- if (status == noErr) {
- /*
- * Initialize the hardware.
- */
- status = NCRResetChip(kNCRResetChipInitialize);
- CheckStatus(status, "\pNCRResetChip failed");
- }
- if (status != noErr)
- PublishInitFailureMsg(status, "\pDriverReplaceCmd failure");
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverFinalizeCmd
- *
- * Process a DoDriverIO finalize command.
- */
- OSErr
- DriverFinalizeCmd(
- DriverFinalInfoPtr driverFinalInfoPtr
- )
- {
- Trace(DriverFinializeCmd);
- (void) DriverSupersededCmd((DriverSupersededInfoPtr) driverFinalInfoPtr, TRUE);
- CLEAR(GLOBAL);
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverSupersededCmd
- *
- * We are shutting down, or being replaced by a later driver. Wait for all I/O to
- * complete and store volatile state in the Name Registry whree it will be retrieved
- * by our replacement.
- */
- OSErr
- DriverSupersededCmd(
- DriverSupersededInfoPtr driverSupersededInfoPtr,
- Boolean calledFromFinalize
- )
- {
- Trace(DriverSupersededCmd);
- UNUSED(driverSupersededInfoPtr);
- UNUSED(calledFromFinalize);
- /*
- * This duplicates DriverKillIOCmd, the correct algorithm would wait for
- * concurrent I/O to complete. Hmm, what about "infinite wait" I/O, such
- * as would be posted by a modem server or socket listener? Note that
- * this section needs to be extended to handle all pending requests.
- *
- * It's safe to call CompleteThisRequest, as that routine uses an atomic
- * operation that allows it to be called when no request is pending without
- * any possible problems. Since it's a secondary interrupt handler, we
- * need to call it through the Driver Services Library.
- *
- * Warning: GLOBAL.perRequestDataPtr will be NULL if initialization fails
- * and the Driver Manager tries to terminate us. When we permit concurrent
- * requests, this will loop on all per-request records.
- */
- if (GLOBAL.perRequestDataPtr == NULL)
- LogString("\pSupersede called before initialization complete");
- else {
- CancelWatchdogTimer(GLOBAL.perRequestDataPtr);
- (void) NCRResetChip(kNCRStopCurrentScript);
- (void) NCRSecondaryInterruptHandler(
- GLOBAL.perRequestDataPtr, (void *) abortErr);
- DisposePerRequestRecord(&GLOBAL.perRequestDataPtr);
- }
- DisposePerRequestRecord(&GLOBAL.perRequestDataPtr);
- DisposeMemoryForScript();
- if (GLOBAL.interruptDisableFunction != NULL) {
- (*GLOBAL.interruptDisableFunction)(
- GLOBAL.interruptSetMember,
- GLOBAL.interruptSetRefcon
- );
- GLOBAL.interruptDisableFunction = NULL;
- }
- CLEAR(GLOBAL);
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverControlCmd
- *
- * Process a PBControl command.
- */
- OSErr
- DriverControlCmd(
- AddressSpaceID addressSpaceID,
- IOCommandID ioCommandID,
- IOCommandKind ioCommandKind,
- CntrlParam *pb
- )
- {
- OSErr status;
- UInt8 initiatorID;
-
- Trace(DriverControlCmd);
- UNUSED(ioCommandKind);
- switch (pb->csCode) {
- case driverPowerLow:
- status = NCRResetChip(kNCRResetChipPowerDown);
- break;
- case driverPowerHigh:
- status = NCRResetChip(kNCRResetChipPowerUp);
- break;
- case kControlDoSCSIBusReset:
- /*
- * NCRPatchScript is here so that the test program can dump
- * the script in its un-patched format.
- */
- status = NCRPatchScript(); /* TEMP */
- if (status != noErr) break; /* TEMP */
- status = NCRStartScript(
- addressSpaceID,
- ioCommandID,
- (ParmBlkPtr) pb,
- kBusResetScript
- );
- break;
- case kControlDoSCSIRundown:
- status = NCRPatchScript(); /* TEMP */
- if (status != noErr) break; /* TEMP */
- status = NCRStartScript(
- addressSpaceID,
- ioCommandID,
- (ParmBlkPtr) pb,
- kSCSIRundownScript
- );
- break;
- case kControlGetOrSetInitiatorID:
- initiatorID = ((NCRDriverInitiatorIDParamPtr) pb)->initiatorID;
- status = SetSCSIInitiatorID(initiatorID);
- break;
- case kControlGoodbye: /* Unused */
- /*
- * We should reset the NCR Chip, but we'll wait for Finalize
- */
- status = noErr;
- break;
- default:
- LogDecimal(pb->csCode, "\pUnknown PBControl csCode");
- status = controlErr; /* Unknown csCode */
- break;
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverStatusCmd
- *
- * Process a PBStatus command. We support the driver gestalt call and our private
- * debugging commands.
- */
- OSErr
- DriverStatusCmd(
- IOCommandID ioCommandID,
- IOCommandKind ioCommandKind,
- CntrlParam *pb
- )
- {
- OSErr status;
- UInt8 initiatorID;
- #define PB (*pb)
-
- Trace(DriverStatusCmd);
- UNUSED(ioCommandID);
- UNUSED(ioCommandKind); /* Always synchronous - actually, we don't care */
- switch (PB.csCode) {
- case csDriverGestaltCode:
- status = DriverGestaltHandler(pb);
- break;
- case kControlGetOrSetInitiatorID:
- status = GetSCSIInitiatorID(&initiatorID);
- if (status == noErr)
- ((NCRDriverInitiatorIDParamPtr) pb)->initiatorID = initiatorID;
- break;
- default:
- LogDecimal(PB.csCode, "\pUnknown PBStatus csCode");
- status = statusErr;
- break;
- }
- return (status);
- #undef PB
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverKillIOCmd stops all I/O for this chip. It's a big hammer, use it wisely.
- * This will need revision when we support concurrent I/O as we must stop all
- * pending requests.
- */
- OSErr
- DriverKillIOCmd(
- ParmBlkPtr pb
- )
- {
- #define REQUEST (GLOBAL.perRequestData)
-
- Trace(DriverKillIOCmd);
- UNUSED(pb);
- /*
- * This presumes that, when NCRResetChip interrupts, it will complete the
- * current request, if any.
- */
- (void) NCRResetChip(kNCRStopCurrentScript);
- return (noErr);
- #undef REQUEST
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverReadCmd
- *
- * The caller passes the data buffer and buffer length in the IOParam record and
- * a pointer to a SCSI NCRSCSIParam in the ioMisc field.
- */
- OSErr
- DriverReadCmd(
- AddressSpaceID addressSpaceID,
- IOCommandID ioCommandID,
- IOCommandKind ioCommandKind,
- ParmBlkPtr pb
- )
- {
- OSErr status;
- #define PB (pb->ioParam)
- #define SCSI (*((NCRSCSIParamPtr) PB.ioMisc))
-
- Trace(DriverReadCmd);
- UNUSED(ioCommandKind);
- if (PB.ioMisc == NULL)
- status = paramErr;
- else if ((SCSI.driverAction & kNCRDriverOutputAllowed) != 0)
- status = scsiRequestInvalid;
- else {
- status = NCRStartScript(
- addressSpaceID,
- ioCommandID,
- pb,
- kSCSICommandScript
- );
- }
- return (status);
- #undef PB
- #undef SCSI
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverReadCmd is not supported as we need the SCSI command and the target ID.
- */
- OSErr
- DriverWriteCmd(
- AddressSpaceID addressSpaceID,
- IOCommandID ioCommandID,
- IOCommandKind ioCommandKind,
- ParmBlkPtr pb
- )
- {
- OSErr status;
- #define PB (pb->ioParam)
- #define SCSI (*((NCRSCSIParamPtr) PB.ioMisc))
-
- Trace(DriverWriteCmd);
- UNUSED(ioCommandKind);
- if (PB.ioMisc == NULL)
- status = paramErr;
- else if ((SCSI.driverAction & kNCRDriverInputAllowed) != 0)
- status = scsiRequestInvalid;
- else {
- status = NCRStartScript(
- addressSpaceID,
- ioCommandID,
- pb,
- kSCSICommandScript
- );
- }
- return (status);
- #undef PB
- #undef SCSI
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverCloseCmd does nothing..
- */
- OSErr
- DriverCloseCmd(
- ParmBlkPtr pb
- )
- {
- Trace(DriverCloseCmd);
- UNUSED(pb);
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DriverOpenCmd does nothing: remember that many applications will open a device, but
- * never close it..
- */
- OSErr
- DriverOpenCmd(
- AddressSpaceID addressSpaceID,
- ParmBlkPtr pb
- )
- {
- Trace(DriverOpenCmd);
- UNUSED(addressSpaceID);
- UNUSED(pb);
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * GetSCSIInitiatorID
- *
- * Retrieve the initiator ID from the Name Registry. This may be called from the
- * driver Status handler. It does not modify the NCR initiator ID (SetSCSIInitiatorID
- * will do this).
- */
- OSErr
- GetSCSIInitiatorID(
- UInt8 *initiatorID
- )
- {
- OSErr status;
- UInt8 driverNVRAMRecord[8];
-
- Trace(GetSCSIInitiatorID);
- status = GetDriverNVRAMProperty(
- &GLOBAL.deviceEntry,
- kCreatorType,
- driverNVRAMRecord
- );
- if (status == noErr)
- *initiatorID = driverNVRAMRecord[0];
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * SetSCSIInitiatorID
- *
- * Store the initiator ID in our global area. It will take effect on the next SCSI
- * request: this may cause a subtle race condition if asynchronous I/O is in progress.
- * This function is really here only to illustrate/test NVRAM configuration.
- *
- * Note: because this may create a property in the Name Registry, it must be called
- * from an application "task" context.
- */
- OSErr
- SetSCSIInitiatorID(
- UInt8 initiatorID
- )
- {
- OSErr status;
- UInt8 driverNVRAMRecord[8];
-
- Trace(SetSCSIInitiatorID);
- if (initiatorID > kMaxSCSIInitiatorID)
- status = paramErr;
- else {
- /*
- * This should really wait until all I/O is complete.
- */
- GLOBAL.initiatorID = initiatorID;
- CLEAR(driverNVRAMRecord);
- driverNVRAMRecord[0] = GLOBAL.initiatorID;
- status = UpdateDriverNVRAMProperty(
- &GLOBAL.deviceEntry,
- kCreatorType,
- driverNVRAMRecord
- );
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * InitializeSCSIInitiatorID
- *
- * Retrieve the initiator ID from our NVRAM property. Called from initialization.
- */
- void
- InitializeSCSIInitiatorID(void)
- {
- OSErr status;
- UInt8 driverNVRAMRecord[8];
-
- Trace(InitializeSCSIInitiatorID);
- CLEAR(driverNVRAMRecord);
- status = RetrieveDriverNVRAMProperty(
- &GLOBAL.deviceEntry,
- kCreatorType,
- driverNVRAMRecord
- );
- switch (status) {
- case noErr: /* Found in NVRAM */
- GLOBAL.initiatorID = driverNVRAMRecord[0];
- break;
- case nrNotFoundErr: /* Not in NVRAM */
- case paramErr: /* Deleted bogus NVRAM property */
- default: /* Some other Name Registry error */
- /*
- * Create the NVRAM property.
- */
- CLEAR(driverNVRAMRecord);
- driverNVRAMRecord[0] = GLOBAL.initiatorID;
- status = CreateDriverNVRAMProperty(
- &GLOBAL.deviceEntry,
- kCreatorType,
- driverNVRAMRecord
- );
- break;
- }
- CheckStatus(status, "\pInitializeSCSIInitiatorID");
- }
-